package com.google.code.chaotic.client;

import static com.google.gwt.user.client.ui.HasHorizontalAlignment.ALIGN_LEFT;
import static com.google.gwt.user.client.ui.HasHorizontalAlignment.ALIGN_RIGHT;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

import com.allen_sauer.gwt.log.client.Log;
import com.google.code.chaotic.dto.CommutingServiceDto;
import com.google.code.chaotic.dto.DtoUtils;
import com.google.code.chaotic.dto.EndType;
import com.google.code.chaotic.dto.HabitInServiceDto;
import com.google.code.chaotic.dto.LatLonDto;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.BorderStyle;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.http.client.URL;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.geocode.DirectionResults;
import com.google.gwt.maps.client.geocode.Geocoder;
import com.google.gwt.maps.client.geocode.Placemark;
import com.google.gwt.maps.client.geocode.Route;
import com.google.gwt.maps.client.geocode.Step;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.geom.LatLngBounds;
import com.google.gwt.maps.client.geom.Size;
import com.google.gwt.maps.client.overlay.Icon;
import com.google.gwt.maps.client.overlay.Polyline;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;

public class Util {

	public static final String COUNTRY_SUFFIX = ".my";

	private static final LatLng VIEWPORT_SOUTH_WEST = LatLng.newInstance( //
			3.004, 101.506);
	private static final LatLng VIEWPORT_NORTH_EAST = LatLng.newInstance( //
			3.264, 101.808);

	//

	public static void clearElement(Element parent) {
		int childCount = DOM.getChildCount(parent);
		for (int i = childCount - 1; i >= 0; i--) {
			DOM.removeChild(parent, DOM.getChild(parent, i));
		}
	}

	public static HorizontalAlignmentConstant getReverseAlignment(
			HorizontalAlignmentConstant alignment) {
		return alignment == ALIGN_RIGHT ? ALIGN_LEFT : ALIGN_RIGHT;
	}

	public static void applyDottedBorder(Widget widget, String color) {
		Style style = widget.getElement().getStyle();
		style.setBorderStyle(BorderStyle.DOTTED);
		style.setBorderWidth(1, Unit.PX);
		style.setBorderColor(color);
	}

	public static void maximize(UIObject uiObject) {
		uiObject.setSize("100%", "100%");
	}

	public static LatLngBounds getCityViewport() {
		LatLngBounds viewport = LatLngBounds.newInstance( //
				VIEWPORT_SOUTH_WEST, VIEWPORT_NORTH_EAST);
		return viewport;
	}

	public static Geocoder createGeocoder() {
		Geocoder geocoder = new Geocoder();
		geocoder.setBaseCountryCode(COUNTRY_SUFFIX);
		// LatLngBounds cityViewport = getCityViewport();
		// geocoder.setViewport(cityViewport);
		return geocoder;
	}

	public static HashMap<String, String> extractParameters(String queryStr) {

		HashMap<String, String> map = new HashMap<String, String>();
		String[] tokens = queryStr.split("\\&");
		Arrays.sort(tokens, new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				return o1.compareTo(o2);
			}
		});
		for (String token : tokens) {
			String[] parts = token.split("\\=");
			String part0 = URL.decodeComponent(parts[0]);
			String part1 = parts.length > 1 ? URL.decodeComponent(parts[1])
					: null;
			map.put(part0, part1);

			char[] padding = new char[8 - part0.length()];
			Arrays.fill(padding, ' ');
			part0 = new StringBuilder().append(padding).append(part0)
					.toString();

			Log.debug("Paramter: " + part0 + "\t =   " + part1);
		}
		return map;
	}

	public static LatLonDto toLatLon(LatLng point) {
		return new LatLonDto(point.getLatitude(), point.getLongitude());
	}

	public static LatLng toLatLng(LatLonDto dto) {
		return LatLng.newInstance(dto.getLatitude(), dto.getLongitude());
	}

	//

	public static void logPolyline(DirectionResults result) {
		StringBuilder polylineSB = new StringBuilder("\nPolyline:\n");
		Polyline polyline = result.getPolyline();
		for (int i = 0; i < polyline.getVertexCount(); i++) {
			LatLng vertex = polyline.getVertex(i);
			polylineSB.append(i).append(" \t - ").append(vertex).append('\n');
		}
		Log.debug(polylineSB.append('\n').toString());
	}

	public static void logSteps(DirectionResults result) {
		StringBuilder stepsSB = new StringBuilder();

		List<Placemark> marks = result.getPlacemarks();
		Placemark start = marks.get(0);
		Placemark end = marks.get(marks.size() - 1);

		stepsSB.append("Org:  \t").append(start.getPoint()).append(" \t") //
				.append(start.getAddress()) //
				.append('\n').append("Steps: \n");

		List<Route> routes = result.getRoutes();
		for (int i = 0; i < routes.size(); i++) {
			Route route = routes.get(i);
			stepsSB.append("Route #").append(i).append(":\n");
			for (int j = 0; j < route.getNumSteps(); j++) {
				Step step = route.getStep(j);
				stepsSB.append(step.getPolylineIndex()).append(" \t- ") //
						.append(step.getLatLng()) //
						.append(" \t- ") //
						.append(step.getDescriptionHtml()) //
						.append('\n');
			}
		}
		stepsSB.append("Dst:  \t").append(end.getPoint()).append(" \t") //
				.append(end.getAddress()) //
				.append('\n');

		stepsSB.append("\nSummary: ").append(result.getSummaryHtml());

		Log.debug(stepsSB.append('\n').toString());
	}

	public static LatLngBounds getBounds(LatLng point1, LatLng point2) {
		double minLat = Math.min(point1.getLatitude(), point2.getLatitude());
		double minLng = Math.min(point1.getLongitude(), point2.getLongitude());
		double maxLat = Math.max(point1.getLatitude(), point2.getLatitude());
		double maxLng = Math.max(point1.getLongitude(), point2.getLongitude());
		return LatLngBounds.newInstance( //
				LatLng.newInstance(minLat, minLng), //
				LatLng.newInstance(maxLat, maxLng));
	}

	public static LatLng meanLatLng(List<LatLng> latLngs) {
		double lat = 0;
		double lon = 0;
		for (LatLng latLng : latLngs) {
			lat += latLng.getLatitude();
			lon += latLng.getLongitude();
		}
		int size = latLngs.size();
		return LatLng.newInstance(lat / size, lon / size);
	}

	public static List<LatLng> toLatLngs(List<LatLonDto> dtos) {

		ArrayList<LatLng> list = new ArrayList<LatLng>(dtos.size());
		for (LatLonDto dto : dtos)
			list.add(toLatLng(dto));

		return list;
	}

	public static void zoomToBounds( //
			MapWidget mapWidget, List<LatLng> tempList) { //

		LatLng firstPoint = tempList.get(0);
		LatLngBounds bounds = LatLngBounds.newInstance(firstPoint, firstPoint);
		for (LatLng latLng : tempList)
			bounds.extend(latLng);

		mapWidget.setZoomLevel(mapWidget.getBoundsZoomLevel(bounds));
	}

	public static List<LatLng> extractLatLngs( //
			CommutingServiceDto fullService, EndType endType, //
			boolean cancelled) {

		List<HabitInServiceDto> hisList = fullService.getHabits();

		return toLatLngs(DtoUtils.extractHabitsEnds( //
				DtoUtils.extractHabits(hisList, cancelled), //
				endType));
	}

	public static List<LatLng> extractAllLatLngs( //
			CommutingServiceDto fullService, EndType endType) {

		List<HabitInServiceDto> hisList = fullService.getHabits();

		return toLatLngs(DtoUtils.extractHabitsEnds( //
				DtoUtils.extractAllHabits(hisList), //
				endType));
	}

	public static void scaleIcon(Icon icon, double scale) {
		icon.setIconSize(Util.getScaledSize(icon.getIconSize(), scale));
		icon.setShadowSize(Util.getScaledSize(icon.getShadowSize(), scale));
	}

	public static Size getScaledSize(Size is, double scale) {
		return Size.newInstance( //
				(int) (is.getWidth() * scale), //
				(int) (is.getHeight() * scale));
	}
}
